/**
* ClassExplorer by Denis Kolyako. Created June 10, 2007. Last update January 12, 2008.
* Visit http://dev.etcs.ru for documentation, updates and more free code.
*
* You may distribute this class freely, provided it is not modified in any way (including
* removing this header or changing the package path).
* 
*
* Please contact etc[at]mail.ru prior to distributing modified versions of this class.
*/
/**
 * ClassExplorer lets you view ActionScript 3 definitions in any swf.
 */
package ru.etcs.utils {
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.net.URLStream;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.utils.describeType;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;
	
	import ru.etcs.events.ClassExplorerEvent;
	import flash.errors.IllegalOperationError;
	
	[Event(name="complete", type="flash.events.Event")]
	[Event(name="open", type="flash.events.Event")]
	[Event(name="ioError", type="flash.events.IOErrorEvent")]
	[Event(name="httpStatus", type="flash.events.HTTPStatusEvent")]
	[Event(name="progress", type="flash.events.ProgressEvent")]
	[Event(name="securityError", type="flash.events.SecurityErrorEvent")]
	[Event(name="incorrectData", type="ru.etcs.utils.ClassExplorerEvent")]
	public final class ClassExplorer extends EventDispatcher {
		
		/*
		* *********************************************************
		* CLASS PROPERTIES
		* *********************************************************
		*
		*/

		/**
		 * @private
		 */
		private static const SWF_TAG:String = 'FWS';		

		/**
		 * @private
		 */
		private static const SWF_COMPRESSED_TAG:String = 'CWS';
		
		/**
		 * @private
		 */
		private var _stream:URLStream;

		/**
		 * @private
		 */
		private var _data:SWFByteArray;

		/**
		 * @private
		 */
		private var _loadedData:ByteArray;

		/**
		 * @private
		 */
		private var _definitionXML:XML;

		/**
		 * @private
		 */
		private var _isLoaded:Boolean;

		/**
		 * @private
		 */
		private var _isLoading:Boolean;

		/**
		 * @private
		 */
		private var _swfVersion:uint;

		/**
		 * @private
		 */
		private var _asTags:Array;

		/*
		* *********************************************************
		* CONSTRUCTOR
		* *********************************************************
		*
		*/
		public function ClassExplorer(request:URLRequest = null) {
			super();
			this._isLoaded = false;
			this._isLoading = false;
			this._data = new SWFByteArray();
			this._loadedData = new ByteArray();
			this._stream = new URLStream();
			this._stream.endian = Endian.LITTLE_ENDIAN;
			this._stream.addEventListener(Event.COMPLETE, this.completeHandler);
			this._stream.addEventListener(Event.OPEN, super.dispatchEvent);
			this._stream.addEventListener(HTTPStatusEvent.HTTP_STATUS, super.dispatchEvent);
			this._stream.addEventListener(IOErrorEvent.IO_ERROR, super.dispatchEvent);
			this._stream.addEventListener(ProgressEvent.PROGRESS, super.dispatchEvent);
			this._stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, super.dispatchEvent);
			
			if (request) {
				this.load(request);
			}
		}
		
		/*
		* *********************************************************
		* PRIVATE METHODS
		* *********************************************************
		*
		*/

		/**
		 * @private
		 */
		private function completeHandler(event:Event):void {
			var swfTypeTag:String;
			this._swfVersion = 0;
			
			if (event) {
				this._stream.readBytes(this._loadedData);
			}
			
			if (this._loadedData.bytesAvailable > 26) {
				swfTypeTag = this._loadedData.readUTFBytes(3);
				this._swfVersion = this._loadedData.readByte();
			}

			if (swfTypeTag != ClassExplorer.SWF_TAG && swfTypeTag != ClassExplorer.SWF_COMPRESSED_TAG) {
				var evt:ClassExplorerEvent = new ClassExplorerEvent(ClassExplorerEvent.INCORRECT_DATA);
				super.dispatchEvent(evt);
				return;
			}
						
			this._loadedData.readUnsignedInt();
			this._loadedData.readBytes(this._data);
			
			if (swfTypeTag == ClassExplorer.SWF_COMPRESSED_TAG) {
				this._data.uncompress();
			}
			
			this.rewind();
			this._isLoaded = true;
			this._isLoading = false;
			this._stream = null;
			super.dispatchEvent(event);
		}
				
		/**
		 * @private
		 */
		private function rewind():void {
			this._data.position = 10; // swf_rect
			var frameRate:uint = this._swfVersion > 7 ? this._data.readUnsignedByte() : this._data.readUnsignedShort();
			var frameCount:uint = this._data.readUnsignedShort();
		}
				
		/**
		 * @private
		 */
		private function findTags():void {
			this._asTags = [];
			this.rewind();
			
			while (this._data.bytesAvailable) {
				var tagBytes:uint = this._data.readUnsignedShort();
				var tagID:uint = tagBytes >> 6;
				var length:uint = tagBytes & 0x3F;
				var isBig:Boolean = length == 0x3F;
				
				
				if (tagID == ASTag.ACTION3 || tagID == ASTag.ACTION3_DEFINE) {
					var asTag:ASTag = new ASTag(this._data, tagID);
					
					if (asTag.isValid) {
						this._asTags.push(asTag);
					}
				} else {
					length = isBig ? this._data.readUnsignedInt() :  length;
					this._data.position += length;
				}
			}			
		}
				
		/*
		* *********************************************************
		* GETTERS
		* *********************************************************
		*
		*/
		/**
		* Returns ByteArray of loaded swf.
		*/
		public function get data():ByteArray {
			if (!this._loadedData) {
				return null;
			}
			
			this._loadedData.position = 0;
			return this._loadedData;
		}

		/*
		* *********************************************************
		* PUBLIC METHODS
		* *********************************************************
		*
		*/
		/**
		 * Builds an definition XML of ABC-code.
		 */
		public function getDefinitionInfo():XML {
			if (this._definitionXML) {
				return this._definitionXML;
			}
			
			this._definitionXML = <definitions/>;
			this.findTags();
			var length:uint = this._asTags.length;

			for (var i:uint = 0;i < length;i+=1) {
				var tag:ASTag = this._asTags[i];
				var tagDefinition:XML = tag.buildDefinition();
				this._definitionXML.appendChild(tagDefinition);
			}
			
			return this._definitionXML;
		}
		
		/**
		 * Initiates loading of an external SWF file from the specified URL.
		 * 
		 * @param request:URLRequest — A URLRequest object specifying the URL to download. If the value of this parameter or the URLRequest.url property of the URLRequest object passed are null, Flash Player throws a null pointer error.  
		 */
		public function load(request:URLRequest):void {
			if (this._isLoading || this._isLoaded) {
				throw new IllegalOperationError('ClassExplorer.load(): this method already called');
			}
			
			this._isLoading = true;
			this._stream.load(request);
		}
		
		/**
		 * Loads from binary data stored in a ByteArray object. 
		 * 
		 * @param bytes:ByteArray — A ByteArray object.
		 */
		public function loadBytes(bytes:ByteArray):void {
			if (this._isLoading || this._isLoaded) {
				throw new IllegalOperationError('ClassExplorer.loadBytes(): data already loaded or loading');
			}
			
			this._isLoading = true;
			bytes.readBytes(this._loadedData);
			bytes.position = 0;
			this._loadedData.position = 0;
			setTimeout(this.completeHandler, 5);
		}

		/**
		 * Closes the stream, causing any download of data to cease.
		 */
		public function close():void {
			if (this._isLoaded || !this._isLoading) {
				return;
			}

			this._stream.close();
		}
	}
}

import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.utils.getQualifiedClassName;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
	
internal class SWFByteArray extends ByteArray {
	
	/*
	* *********************************************************
	* PROPERTIES
	* *********************************************************
	*
	*/
	public static const SWF_TAG:String = 'FWS';
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function SWFByteArray() {
		super();
		super.endian = Endian.LITTLE_ENDIAN;
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function readASInt():int {
		var pos:uint = super.position;
		var out:int = 0;
		var i:uint = 0;
		var byte:uint = this[pos];
		
		if (byte < 0x80) {
			super.position = pos+1;
			return byte;
		}
		
		pos += 1;
		
		while (byte >= 0x80) {
			out += (byte-0x80) << (i * 7);
			i += 1;
			byte = this[pos];
			pos += 1;
		}
		
		out += (byte << (i * 7)); 
	   	super.position = pos;
		return out;
	}
	
	public function readSignedInt24():int {
		var pos:uint = super.position;
		var out:int = (this[pos] << 16);
		out += (this[pos+=1] << 8);
		out += this[pos+=1]; 
		
		if (out >= 0x808080) {
			out -= 0xFFFFFF;
		}
		
		super.position = pos+1;
		return out;
	}
	
	public function readUnsignedInt64():Number {
		var n1:Number = super.readUnsignedInt();
		var n2:Number = super.readUnsignedInt();
		return n2 * 0x100000000 + n1;	
	}

	public function readString():String {
		var i:uint = super.position;
		while (this[i] && (i+=1));
		var str:String = super.readUTFBytes(i - super.position);
		super.position = i+1; 
		return str;
	}

	public function writeBytesFromString(bytesHexString:String):void {
		var length:uint = bytesHexString.length;
		
		for (var i:uint = 0;i<length;i+=2) {
			var hexByte:String = bytesHexString.substr(i,2);
			var byte:uint = parseInt(hexByte, 16);
			writeByte(byte);
		}
	}
	
	public function traceArray(array:ByteArray):String { // for debug
		var out:String = '';
		var pos:uint = array.position;
		array.position = 0;
		var i:uint = 0;
		
		while (array.bytesAvailable) {
			var str:String = array.readUnsignedByte().toString(16).toUpperCase();
			str = str.length < 2 ? '0'+str : str;
			out += str+' ';
		}
		
		array.position = pos;
		return out;
	}
}

internal final class ASTag {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const ACTION3_DEFINE:uint = 82;
	public static const ACTION3:uint = 72;
	public static const MINOR_VERSION:uint = 0x0010;
	public static const MAJOR_VERSION:uint = 0x002E;
			
	/**
	 * @private
	 */
	private var _isValid:Boolean;

	/**
	 * @private
	 */
	private var _offset:uint;

	/**
	 * @private
	 */
	private var _length:uint;

	/**
	 * @private
	 */
	private var _data:SWFByteArray;

	/**
	 * @private
	 */
	private var _intOffset:uint;

	/**
	 * @private
	 */
	private var _uintOffset:uint;

	/**
	 * @private
	 */
	private var _doubleOffset:uint;

	/**
	 * @private
	 */
	private var _stringOffset:uint;

	/**
	 * @private
	 */
	private var _namespaceOffset:uint;

	/**
	 * @private
	 */
	private var _nsSetOffset:uint;

	/**
	 * @private
	 */
	private var _multinameOffset:uint;

	/**
	 * @private
	 */
	private var _methodOffset:uint;

	/**
	 * @private
	 */
	private var _metadataOffset:uint;

	/**
	 * @private
	 */
	private var _instanceOffset:uint;

	/**
	 * @private
	 */
	private var _classOffset:uint;

	/**
	 * @private
	 */
	private var _scriptOffset:uint;

	/**
	 * @private
	 */
	private var _methodBodyOffset:uint;

	/**
	 * @private
	 */
	private var _intTable:Array;

	/**
	 * @private
	 */
	private var _uintTable:Array;

	/**
	 * @private
	 */
	private var _doubleTable:Array;

	/**
	 * @private
	 */
	private var _stringTable:Array;

	/**
	 * @private
	 */
	private var _namespaceTable:Array;

	/**
	 * @private
	 */
	private var _nsSetTable:Array;

	/**
	 * @private
	 */
	private var _multinameTable:Array;

	/**
	 * @private
	 */
	private var _methodTable:Array;

	/**
	 * @private
	 */
	private var _metadataTable:Array;

	/**
	 * @private
	 */
	private var _instanceTable:Array;

	/**
	 * @private
	 */
	private var _classTable:Array;

	/**
	 * @private
	 */
	private var _scriptTable:Array;

	/**
	 * @private
	 */
	private var _methodBodyTable:Array;

	/**
	 * @private
	 */
	private var _definition:XML;
	
	/**
	 * @private
	 */
	private var _scriptName:String;
	
	/**
	 * @private
	 */
	private var _methodMap:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASTag(swfData:SWFByteArray, kind:uint) {
		var currentPosition:uint = swfData.position;
		var version:uint = 0;
		var i:uint;
		this._isValid = false;
		this._length = swfData.readUnsignedInt();
		
		if (swfData.bytesAvailable >= this._length) {
			this._data = new SWFByteArray();
			swfData.readBytes(this._data, 0, this._length);
			
			if (kind == ASTag.ACTION3_DEFINE) {
				var flags:uint = this._data.readUnsignedInt();
				this._scriptName = this._data.readString();
			} else {
				this._scriptName = '';
			}
			
			var minorVersion:uint = this._data.readShort();
			var majorVersion:uint = this._data.readShort();

			if (minorVersion == ASTag.MINOR_VERSION && majorVersion == ASTag.MAJOR_VERSION) {
				this._isValid = true;
				this._intOffset = this._data.position;
				this._offset = currentPosition-2;
			}
		}
		
		if (!this._isValid) {
			swfData.position = currentPosition;
		}
	}
	
	public function get isValid():Boolean {
		return this._isValid;
	}
	
	public function get intTable():Array {
		return this._intTable;
	}
	
	public function get uintTable():Array {
		return this._uintTable;	
	}
	
	public function get doubleTable():Array {
		return this._doubleTable;
	}
	
	public function get stringTable():Array {
		return this._stringTable;
	}
	
	public function get namespaceTable():Array {
		return this._namespaceTable;
	}
	
	public function get nsSetTable():Array {
		return this._nsSetTable;
	}
	
	public function get multinameTable():Array {
		return this._multinameTable;
	}
	
	public function get methodTable():Array {
		return this._methodTable;
	}

	public function get metadataTable():Array {
		return this._metadataTable;
	}
	
	public function get instanceTable():Array {
		return this._instanceTable;
	}

	public function get classTable():Array {
		return this._classTable;
	}
	
	public function get scriptTable():Array {
		return this._scriptTable;
	}
	
	public function get methodBodyTable():Array {
		return this._methodBodyTable;
	}
	
	public function buildDefinition():XML {
		if (this._definition != null) {
			return this._definition;
		}
		
		this.parse();
		this._definition = <definition name={this._scriptName}/>;
		var length:uint = this._instanceTable.length;
		var instanceSuperMultiname:ASMultiname;
		var instanceNamespace:ASNamespace;
		var instanceSuperNamespace:ASNamespace;
		var instanceInterface:ASMultiname;
		var instanceInterfaceNamespaceSet:ASNsSet;
		var instanceInterfaceNamespace:ASNamespace;
		var itemXML:XML;
		var i:uint;
		var j:uint;
		var k:uint;
		
		for (i = 0;i < length;i+=1) {
			var instanceItem:ASInstance = this._instanceTable[i];
			var classItem:ASClass = this._classTable[i];
			var instanceDefinition:XML = <type/>;
			var instanceMultiname:ASMultiname = instanceItem.multiName;
			var isInternal:Boolean = false;
			var isMatch:Boolean = true;

			if (instanceMultiname.ns) {
				instanceNamespace = this._namespaceTable[instanceMultiname.ns-1];
				
				switch (instanceNamespace.kind) {
					case ASNamespace.PACKAGE:
					case ASNamespace.PACKAGE_INTERNAL:
						instanceDefinition.@name = instanceNamespace.stringName.length ? instanceNamespace.stringName+'::'+instanceMultiname.stringName : instanceMultiname.stringName;
						instanceDefinition.@packageName = instanceNamespace.stringName;
					break;
					default:
						if (ASInstance.PROTECTED & instanceItem.flags) { // is internal?
							instanceDefinition.@name = this._namespaceTable[instanceItem.ns-1].stringName.replace(':','::');
							isInternal = true;
						} else {
							
							instanceDefinition.@name = instanceMultiname.stringName;
						}
						instanceDefinition.@packageName = '';
				}
			} else {
				instanceDefinition.@name = instanceMultiname.stringName;
				instanceDefinition.@packageName = '';
			}
			
			instanceDefinition.@id = i;

			if (instanceItem.superName) {
				instanceSuperMultiname = this._multinameTable[instanceItem.superName-1];
				
				if (instanceSuperMultiname.ns) {
					instanceSuperNamespace = this._namespaceTable[instanceSuperMultiname.ns-1];
					
					switch (instanceSuperNamespace.kind) {
						case ASNamespace.PACKAGE:
						case ASNamespace.PACKAGE_INTERNAL:
							instanceDefinition.@superName = instanceSuperNamespace.stringName.length ? instanceSuperNamespace.stringName+'::'+instanceSuperMultiname.stringName : instanceSuperMultiname.stringName;
						break;
						default:
							instanceDefinition.@superName = instanceSuperMultiname.stringName;
					}
				} else {
					instanceDefinition.@superName = instanceSuperMultiname.stringName;
				}
			} else {
				instanceDefinition.@superName = 'Class';
			}
			
			instanceDefinition.@isDynamic = !(ASInstance.SEALED & instanceItem.flags);
			instanceDefinition.@isFinal = Boolean(ASInstance.FINAL & instanceItem.flags);

			if (ASInstance.INTERFACE & instanceItem.flags) {
				instanceDefinition.setName('interfaceInstance');
			} else if (isInternal) {
				instanceDefinition.setName('internalClass');
			} else {
				instanceDefinition.setName('classInstance');					
			}
			
			for (j = 0;j < instanceItem.interfaces.length;j++) {
				itemXML = <implementsInterface/>;
				instanceInterface = this._multinameTable[instanceItem.interfaces[j]-1];
				
				switch (instanceInterface.kind) {
					case ASMultiname.MULTINAME:
					case ASMultiname.MULTINAME_A:
					case ASMultiname.MULTINAME_L:
					case ASMultiname.MULTINAME_LA:
						instanceInterfaceNamespaceSet = this._nsSetTable[instanceInterface.nsSet-1];
						instanceInterfaceNamespace = this._namespaceTable[instanceInterfaceNamespaceSet.namespaces[0]-1]; // maybe wrong?
						itemXML.@type = instanceInterfaceNamespace.stringName.length ? instanceInterfaceNamespace.stringName+'::'+instanceInterface.stringName : instanceInterface.stringName;
					break;
					case ASMultiname.QNAME:
					break;
				}
				
				instanceDefinition.appendChild(itemXML);
			}

			this.buildTraits(classItem.traits, instanceDefinition, true);
			this.buildTraits(instanceItem.traits, instanceDefinition);
			this._definition.appendChild(instanceDefinition);
		}
		
		for (i = 0;i < this._scriptTable.length;i++) {
			var scriptItem:ASScript = this._scriptTable[i];
			var scriptDefinition:XML = <script/>
			var scriptMethod:ASMethod = this._methodTable[scriptItem.init];
			scriptDefinition.@methodID = scriptMethod.id;
			this.buildTraits(scriptItem.traits, scriptDefinition);
			this._definition.appendChild(scriptDefinition);
		}
		
		return this._definition;
	}

	/*
	* *********************************************************
	* PRIVATE METHODS
	* *********************************************************
	*
	*/

	/**
	 * @private
	 */
	private function parse():void {
		if (this._isValid) {
			this._data.position = this._intOffset;
			this.buildIntTable();
			this._uintOffset = this._data.position;
			this.buildUintTable();
			this._doubleOffset = this._data.position;
			this.buildDoubleTable();
			this._stringOffset = this._data.position;
			this.buildStringTable();
			this._namespaceOffset = this._data.position;
			this.buildNamespaceTable();
			this._nsSetOffset = this._data.position;
			this.buildNsSetTable();
			this._multinameOffset = this._data.position;
			this.buildMultinameTable();
			this._methodOffset = this._data.position;
			this.buildMethodTable();
			this._metadataOffset = this._data.position;
			this.buildMetadataTable();
			this._instanceOffset = this._data.position;
			this.buildInstanceTable();
			this._classOffset = this._data.position;
			this.buildClassTable();
			this._scriptOffset = this._data.position;
			this.buildScriptTable();
			this._methodBodyOffset = this._data.position;
			this.buildMethodBodyTable();
			/* trace('int: \n'+this._intTable.join('\n')); // for debug purposes
			trace('------------------------------------------');				
			trace('uint: \n'+this._uintTable);
			trace('------------------------------------------');				
			trace('double: \n'+this._doubleTable.join('\n'));
			trace('------------------------------------------');
			trace('string: \n'+this._stringTable.join('\n'));
			trace('------------------------------------------');				
			trace('namespace: \n'+this._namespaceTable.join('\n'));
			trace('------------------------------------------');
			trace('ns set: \n'+this._nsSetTable.join('\n'));
			trace('------------------------------------------');				
			trace('multiname: \n'+this._multinameTable.join('\n'));
			trace('------------------------------------------');				
			trace('method: \n'+this._methodTable.join('\n'));
			trace('------------------------------------------');				
			trace('metadata: \n'+this._metadataTable.join('\n'));
			trace('------------------------------------------');		
			trace('instance: \n'+this._instanceTable.join('\n'));
			trace('------------------------------------------');				
			trace('class: \n'+this._classTable.join('\n'));
			trace('------------------------------------------');				
			trace('script: \n'+this._scriptTable.join('\n'));
			trace('------------------------------------------');
			trace('method body: \n'+this._methodBodyTable.join('\n'));
			trace('------------------------------------------');	
			trace('bytesAvailable: '+this._data.bytesAvailable); // */
		}
	}

	/**
	* @private
	*/
	private function setNamespaceParams(traitMultiname:ASMultiname, itemXML:XML, isStatic:Boolean = false):void {
		if (traitMultiname.ns) {
			var traitNamespace:ASNamespace = this._namespaceTable[traitMultiname.ns-1];
			itemXML.@namespaceType = (isStatic && traitNamespace.kind != ASNamespace.STATIC_PROTECTED ? 'static ' : '')+String(ASNamespace.KIND_TABLE[traitNamespace.kind]).toLowerCase().replace('package','public').replace('namespace','public');
			itemXML.@namespaceName = traitNamespace.stringName;
		} else {
			itemXML.@namespaceType = isStatic ? 'static' : null;
			itemXML.@namespaceName = null;
		}
	}
	
	/**
	* @private
	*/
	private function getInstanceName(multiname:ASMultiname, useDotName:Boolean = false):String {
		if (multiname) {
			if (multiname.ns) {
				var nameSpace:ASNamespace = this._namespaceTable[multiname.ns-1];
				
				switch (nameSpace.kind) {
					case ASNamespace.PACKAGE:
					case ASNamespace.PACKAGE_INTERNAL:
						return nameSpace.stringName.length ? nameSpace.stringName+(useDotName ? '.' : '::')+multiname.stringName : multiname.stringName;
					break;
					default:
						return multiname.stringName;
				}
			} else {
				return multiname.stringName;
			}
		} else {
			return '*';
		}
	}

	/**
	* @private
	*/
	private function buildTraits(allTraits:Array, instanceDefinition:XML, isStatic:Boolean = false):void {
		var itemXML:XML;
		var trait:ASTrait;
		var traitMultiname:ASMultiname;
		var method:ASMethod;
		var body:ASMethodBody;
		var paramMultiname:ASMultiname;
		var attribute:XML;
		var paramXML:XML;
		var searchXML:XMLList;			
		var optionalParam:ASOptionalParam;
		var j:uint;
		var k:uint;
		var isMatch:Boolean = true;
		
		loop: for (j = 0;j < allTraits.length;j++) {
			trait = allTraits[j];
			itemXML = <trait/>;
			traitMultiname = trait.multiName;

			switch (trait.kind) {
				case ASTrait.SLOT:
				case ASTrait.CONST:
					itemXML.setName((trait.kind == ASTrait.SLOT) ? 'variable' : 'constant');
					itemXML.@name = traitMultiname.stringName;
					itemXML.@multinameID = trait.multiName.id;
					itemXML.@type = this.getInstanceName(trait.typeMultiname);
					itemXML.@value = trait.vValue;
					this.setNamespaceParams(traitMultiname, itemXML, isStatic);
				break;
				case ASTrait.CLASS:
					itemXML.setName('classTrait');
					itemXML.@name = this.getInstanceName(traitMultiname);
					this.setNamespaceParams(traitMultiname, itemXML, isStatic);
				break;
				case ASTrait.SETTER:
				case ASTrait.GETTER:
					method = this._methodTable[trait.methodID];
					itemXML.setName('accessor');
					itemXML.@name = traitMultiname.stringName;
					itemXML.@methodID = trait.methodID;
					itemXML.@type = this.getInstanceName((trait.kind == ASTrait.SETTER) ? method.params[0] : method.returnTypeMultiName);
					itemXML.@isOverride = Boolean(ASTrait.OVERRIDE & trait.flags);
					this.setNamespaceParams(traitMultiname, itemXML, isStatic);
					itemXML.@access = (trait.kind == ASTrait.SETTER) ? 'writeonly' : 'readonly';	
				break;
				case ASTrait.METHOD:
				case ASTrait.FUNCTION:
					if (trait.kind == ASTrait.METHOD) {
						method = this._methodTable[trait.methodID];
						itemXML.setName('method');
					} else {
						method = this._methodTable[trait.functionID];
						itemXML.setName('functionMethod');							
					}
					
					body = this._methodBodyTable[this._methodMap[method.id]];
					itemXML.@name = traitMultiname.stringName;
					itemXML.@methodID = trait.methodID;
					itemXML.@returnType = this.getInstanceName(method.returnTypeMultiName);
					itemXML.@isOverride = Boolean(ASTrait.OVERRIDE & trait.flags);
					itemXML.@hasOptional = Boolean(ASMethod.HAS_OPTIONAL & method.flags);
					itemXML.@needRest = Boolean(ASMethod.NEED_REST & method.flags);
					this.setNamespaceParams(traitMultiname, itemXML, isStatic);
					
					for (k = 0;k < method.paramCount;k+=1) {
						paramMultiname = method.params[k];
						paramXML = <parameter/>;
						var tempTrait:ASTrait = body ? body.traits[body.traitsSlotMap[k+1]] : null;
						
						if (tempTrait) {
							paramXML.@name = tempTrait.multiName.stringName;
						} else {
							paramXML.@name = '';	
						}
						
						paramXML.@type = this.getInstanceName(paramMultiname);
						paramXML.@optional = false;
						paramXML.@value = undefined;
						itemXML.appendChild(paramXML);
					}
					
					var optionalLength:uint = method.optionalParams.length;
					
					for (k = 0;k < optionalLength;k+=1) {
						optionalParam = method.optionalParams[k];
						paramXML = itemXML.parameter[method.paramCount-optionalLength+k];
						paramXML.@value = optionalParam.value;
						paramXML.@optional = true;
					}
					
					if (!body) {
						break;
					}
					
					var exceptionLength:uint = body.exceptions.length;
					
					for (k = 0;k < exceptionLength;k+=1) {
						var exception:ASException = body.exceptions[k];
						var exceptionXML:XML = <catchesException name={exception.stringName} type={exception.excTypeName}/>;
						itemXML.appendChild(exceptionXML);
					}
				break;
			}
							
			if (trait.flags & ASTrait.METADATA) {
				itemXML.appendChild(this.getMetadata(trait.metadata));
			}

			instanceDefinition.appendChild(itemXML);
		}
	}

	/**
	* @private
	*/
	private function getMetadata(metadataIDs:Array):XMLList {
		var idCount:uint = metadataIDs.length;
		var metaList:XMLList = new XMLList();
		
		for (var i:uint = 0;i < idCount;i+=1) {
			var xml:XML = <metadata/>;
			var meta:ASMetadata = this._metadataTable[metadataIDs[i]];
			xml.@name = meta.stringName;
			var list:XMLList = xml.children();
			var count:uint = meta.items.length;
			
			for (var j:uint = 0;j < count;j+=1) {
				var item:ASMetadataItem = meta.items[j];
				list += <arg key={item.keyString} value={item.valueString}/>;
			}
			
			xml.appendChild(list);
			metaList += xml;
		}
		
		return metaList;
	}

	/**
	* @private
	*/
	private function getValue(index:uint, kind:uint):* {
		if (!index) {
			return undefined;
		}
		
		index-=1;
		var value:* = undefined;
		
		switch (kind) {
			case ASOptionalParam.INT:
			case ASOptionalParam.UINT:
				value = this._intTable[index];
			break;
			case ASOptionalParam.DOUBLE:
				value = this._doubleTable[index];							
			break;
			case ASOptionalParam.UTF8:
				value = this._stringTable[index];		
			break;
			case ASOptionalParam.TRUE:
				value = true;
			break;
			case ASOptionalParam.FALSE:
				value = false;
			break;
			case ASOptionalParam.NULL:
				value = null;
			break;
			case ASOptionalParam.UNDEFINED:
				value = undefined;
			break;
			case ASOptionalParam.NAMESPACE:
			case ASOptionalParam.PACKAGE_NAMESPACE:
			case ASOptionalParam.PACKAGE_INTERNAL_NS:
			case ASOptionalParam.PROTECTED_NAMESPACE:
			case ASOptionalParam.EXPLICIT_NAMESPACE:
			case ASOptionalParam.STATIC_PROTECTED_NS:
			case ASOptionalParam.PRIVATE_NAMESPACE:
				value = this._namespaceTable[index];
			break;						  
		}			  
			  
		return value;
	}
		
	/**
	* @private
	*/
	private function buildTraitsTable():Array {
		var traits:Array = [];
		var traitCount:uint = this._data.readASInt();

		while (traitCount) {
			var trait:ASTrait = new ASTrait();
			var name:uint = this._data.readASInt();
			var kind:uint = this._data.readUnsignedByte();
			var upperBits:uint = kind >> 4;
			var lowerBits:uint = kind & 0xF;
			trait.name = name;
			trait.multiName = this._multinameTable[name-1];
			trait.kind = lowerBits;
			trait.flags = upperBits;

			switch (lowerBits) {
				case ASTrait.SLOT:
				case ASTrait.CONST:
					trait.slotID = this._data.readASInt();
					trait.typeName = this._data.readASInt();
					trait.vIndex = this._data.readASInt();
					trait.typeMultiname = this._multinameTable[trait.typeName-1];
					
					if (trait.vIndex) {
						trait.vKind = this._data.readUnsignedByte();		
						trait.vValue = this.getValue(trait.vIndex, trait.vKind);
					}
				break;
				case ASTrait.CLASS:
					trait.slotID = this._data.readASInt();
					trait.classID = this._data.readASInt();
				break;
				case ASTrait.FUNCTION:
					trait.slotID = this._data.readASInt();
					trait.functionID = this._data.readASInt();
				break;
				case ASTrait.METHOD:
				case ASTrait.GETTER:
				case ASTrait.SETTER:
					trait.dispID = this._data.readASInt();
					trait.methodID = this._data.readASInt();	
				break;
			}

			if (ASTrait.METADATA & upperBits) {
				var metadataCount:uint = this._data.readASInt();
				
				while (metadataCount) {
					trait.metadata[trait.metadata.length] = this._data.readASInt();
					metadataCount -= 1;
				}
			}
			
			traits[traits.length] = trait;
			traitCount -= 1;
		}

		return traits;
	}

	/**
	* @private
	*/
	private function buildIntTable():void {
		var count:uint = this._data.readASInt();
		this._intTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				this._intTable.push(this._data.readASInt());
				count -= 1;
			}
		}
	}
	
	/**
	* @private
	*/
	private function buildUintTable():void {
		var count:uint = this._data.readASInt();
		this._uintTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				this._uintTable[this._uintTable.length] = this._data.readASInt();
				count -= 1;
			}	
		}
	}
	
	/**
	* @private
	*/
	private function buildDoubleTable():void {
		var count:uint = this._data.readASInt();
		this._doubleTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				this._doubleTable[this._doubleTable.length] = this._data.readDouble();
				count -= 1;
			}	
		}
	}
	
	/**
	* @private
	*/
	private function buildStringTable():void {
		var count:uint = this._data.readASInt();
		this._stringTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				this._stringTable[this._stringTable.length] = this._data.readUTFBytes(this._data.readASInt());
				count -= 1;
			}
		}
	}
	
	/**
	* @private
	*/
	private function buildNamespaceTable():void {
		var count:uint = this._data.readASInt();
		var id:uint = 1;
		this._namespaceTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				var kind:uint = this._data.readUnsignedByte();
				var name:uint = this._data.readASInt();
				var ns:ASNamespace = new ASNamespace(id, kind, name);
				ns.stringName = name ? this._stringTable[name-1] : ''; 
				this._namespaceTable[this._namespaceTable.length] = ns;
				id+=1;
				count -= 1;
			}
		}
	}
	
	/**
	* @private
	*/
	private function buildNsSetTable():void {
		var count:uint = this._data.readASInt();
		var id:uint = 1;
		this._nsSetTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				var nsCount:uint = this._data.readUnsignedByte();
				var nsSet:ASNsSet = new ASNsSet(id);
				
				while (nsCount) {
					nsSet.namespaces[nsSet.namespaces.length] = this._data.readASInt();
					nsCount -= 1;
				}
				
				this._nsSetTable[this._nsSetTable.length] = nsSet;
				id+=1;
				count -= 1;
			}
		}		
	}
	
	/**
	* @private
	*/
	private function buildMultinameTable():void {
		var count:uint = this._data.readASInt();
		var id:uint = 1;
		this._multinameTable = [];
		
		if (count > 1) {
			count-=1;
			
			while (count) {
				var kind:uint = this._data.readASInt();
				var mn:ASMultiname = new ASMultiname(id, kind);
				
				switch (kind) {
					case ASMultiname.QNAME:
					case ASMultiname.QNAME_A:
						mn.ns = this._data.readASInt();
						mn.name = this._data.readASInt();
						
						if (mn.name) {
							mn.stringName = this._stringTable[mn.name-1];
						} else {
							mn.stringName = '*';
						}
					break;	
					case ASMultiname.RTQNAME:
					case ASMultiname.RTQNAME_A:
						mn.name = this._data.readASInt();

						if (mn.name) {
							mn.stringName = this._stringTable[mn.name-1];
						} else {
							mn.stringName = '*';
						}
					break;	
					case ASMultiname.RTQNAME_L:
					case ASMultiname.RTQNAME_LA:
					break;	
					case ASMultiname.MULTINAME:
					case ASMultiname.MULTINAME_A:
						mn.name = this._data.readASInt();
						mn.nsSet = this._data.readASInt();

						if (mn.name) {
							mn.stringName = this._stringTable[mn.name-1];
						} else {
							mn.stringName = '*';
						}
					break;	
					case ASMultiname.MULTINAME_L:
					case ASMultiname.MULTINAME_LA:
						mn.nsSet = this._data.readASInt();
					break;	
				}
				
				this._multinameTable[this._multinameTable.length] = mn;
				id+=1;
				count -= 1;
			}
		}
	}
	
	/**
	* @private
	*/
	private function buildMethodTable():void {
		var id:uint = 0;
		var count:uint = this._data.readASInt();
		this._methodTable = [];
		
		while (count) {
			var method:ASMethod = new ASMethod(id);
			var paramsCount:int = this._data.readASInt();
			method.paramCount = paramsCount;
			method.returnType = this._data.readASInt();
			
			if (method.returnType) {
				method.returnTypeMultiName = this._multinameTable[method.returnType-1];
			}
			
			while (paramsCount) {
				var paramID:uint = this._data.readASInt();
				
				if (paramID) {
					method.params[method.params.length] = this._multinameTable[paramID-1];
				} else {
					method.params[method.params.length] = new ASMultiname(0, 0);
				}
				
				paramsCount -= 1;
			}
									
			method.name = this._data.readASInt();
			
			if (method.name) {
				method.stringName = this._stringTable[method.name-1];
			}
			
			method.flags = this._data.readUnsignedByte();
			
			if (ASMethod.HAS_OPTIONAL & method.flags) {
				var optionParamCount:uint = this._data.readASInt();
				
				while (optionParamCount) {
					var param:ASOptionalParam = new ASOptionalParam(this._data.readASInt(), this._data.readASInt());
					param.value = getValue(param.val, param.kind);
					method.optionalParams[method.optionalParams.length] = param;
					optionParamCount -= 1;
				}
			}
			
			if (ASMethod.HAS_PARAM_NAMES & method.flags) {
				paramsCount = method.paramCount;
				
				while (paramsCount) {
					method.paramNames[method.paramNames.length] = this._data.readASInt();
					paramsCount -= 1;
				}
			}

			this._methodTable[this._methodTable.length] = method;
			id+=1;
			count -= 1;
		}
	}
	
	/**
	* @private
	*/
	private function buildMetadataTable():void {
		var id:uint = 0;
		var i:uint = 0;
		var count:uint = this._data.readASInt();
		this._metadataTable = [];
		
		while (count) {
			var metadata:ASMetadata = new ASMetadata(id, 0);
			metadata.name = this._data.readASInt();
			metadata.stringName = this._stringTable[metadata.name-1];
			var itemsCount:uint = this._data.readASInt();
			var keys:Array = []; // There are no pairs key/value, there are keys, then values. Sorry, Adobe, you make a mistake in your AVM2 overview…
			
			for (i = 0;i < itemsCount;i+=1) {
				keys[keys.length] = this._data.readASInt();
			}
			
			for (i = 0;i < itemsCount;i+=1) {
				var item:ASMetadataItem = new ASMetadataItem();
				item.key = keys[i];
				item.value = this._data.readASInt();
				
				if (item.key) {
					item.keyString = this._stringTable[item.key-1];
				}
				
		   		item.valueString = this._stringTable[item.value-1];
				metadata.items[metadata.items.length] = item;
			}
			
			this._metadataTable[this._metadataTable.length] = metadata;
			id+=1;
			count -= 1;
		}				
	}
		
	/**
	* @private
	*/
	private function buildInstanceTable():void {
		var id:uint = 0;
		var count:uint = this._data.readASInt();
		this._instanceTable = [];

		while (count) {
			var name:uint = this._data.readASInt();
			var superName:uint = this._data.readASInt();
			var flags:uint = this._data.readUnsignedByte();
			var asInstance:ASInstance = new ASInstance(id, name, superName, flags);
			
			if (name) {
				asInstance.multiName = this._multinameTable[name-1];
			}
			
			if (superName) {
				asInstance.multiSuperName = this._multinameTable[superName-1];
			}
				
			if (ASInstance.PROTECTED & flags) {
				var ns:uint = this._data.readASInt();
				asInstance.ns = ns;
				
				if (ns) {
					asInstance.nameSpace = this._namespaceTable[ns-1];
				}
			}
				
			var interfaceCount:uint = this._data.readASInt();
			
			while (interfaceCount) {
				asInstance.interfaces[asInstance.interfaces.length] = this._data.readASInt();
				interfaceCount -= 1;
			}
		
			asInstance.iinit = this._data.readASInt();
			asInstance.traits = this.buildTraitsTable();
			this._instanceTable[this._instanceTable.length] = asInstance;
			id+=1;
			count -= 1;
		}
	}
	
	/**
	* @private
	*/
	private function buildClassTable():void {
		var id:uint = 0;
		var count:uint = instanceTable.length;
		this._classTable = [];

		while (count) {
			var asClass:ASClass = new ASClass(id, this._data.readASInt());
			asClass.traits = this.buildTraitsTable();
			this._classTable[this._classTable.length] = asClass;
			id+=1;
			count -= 1;
		}
	}
	
	/**
	* @private
	*/
	private function buildScriptTable():void {
		var id:uint = 0;
		var count:uint = this._data.readASInt();
		this._scriptTable = [];

		while (count) {
			var script:ASScript = new ASScript(id, this._data.readASInt());
			script.traits = this.buildTraitsTable();
			this._scriptTable[this._scriptTable.length] = script;
			id+=1;
			count -= 1;
		}
	}
	
	/**
	* @private
	*/
	private function buildMethodBodyTable():void {
		var id:uint = 0;
		var count:uint = this._data.readASInt();
		this._methodBodyTable = [];
		this._methodMap = [];
		
		while (count) {
			var method:uint = this._data.readASInt();
			var body:ASMethodBody = new ASMethodBody(id, method);
			this._methodMap[method] = id;
			var asMethod:ASMethod = this._methodTable[method];
			body.methodName = asMethod.stringName;
			body.maxStack = this._data.readASInt();
			body.localCount = this._data.readASInt();
			body.initScopeDepth = this._data.readASInt();
			body.maxScopeDepth = this._data.readASInt();
			var codeLength:uint = this._data.readASInt();
			this._data.readBytes(body.code, 0, codeLength);
			var exceptionCount:uint = this._data.readASInt();
			
			while (exceptionCount--) {
				var exception:ASException = new ASException();
				exception.from = this._data.readASInt();
				exception.to = this._data.readASInt();
				exception.target = this._data.readASInt();
				exception.excType = this._data.readASInt();
				
				if (exception.excType) {
					exception.excTypeName = this.getInstanceName(this._multinameTable[exception.excType-1]);
				}
				
				exception.name = this._data.readASInt();
				
				if (exception.name) {
					exception.stringName = this.getInstanceName(this._multinameTable[exception.name-1]);
				}
				
				body.exceptions[body.exceptions.length] = exception;
			}
			
			body.traits = buildTraitsTable();
			var traitsLength:uint = body.traits.length;
			
			for (var i:uint = 0;i < traitsLength;i+=1) {
				var trait:ASTrait = body.traits[i];
				body.traitsSlotMap[trait.slotID] = i;
			}
			
			this._methodBodyTable[this._methodBodyTable.length] = body;
			id+=1;
			count -= 1;
		}
	}
}

internal function initClassKinds(definition:Object):void {
		var xml:XML = describeType(definition);
		var list:XMLList = xml.constant.@name;
		
		if (!('KIND_TABLE' in definition)) {
			return;
		}
		
		for each (var name:String in list) {
			definition.KIND_TABLE[definition[name]] = name.replace(/_/g,' ');		
		}
}

internal final class ASNamespace {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const PRIVATE:uint = 0x05;
	public static const NAMESPACE:uint = 0x08;
	public static const PACKAGE:uint = 0x16;
	public static const PACKAGE_INTERNAL:uint = 0x17;
	public static const PROTECTED:uint = 0x18;
	public static const EXPLICIT:uint = 0x19;
	public static const STATIC_PROTECTED:uint = 0x1A;
	public static const KIND_TABLE:Array = [];
	
	initClassKinds(ASNamespace);

	public var kind:uint;
	public var name:uint;
	public var stringName:String = '*';
	public var id:uint;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASNamespace(id:uint = 0, kind:uint = 0, name:uint = 0) {
		this.id = id;
		this.kind = kind;
		this.name = name;
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASNamespace id: '+this.id
		+' kind: '+this.kind.toString(16)+':'+ASNamespace.KIND_TABLE[this.kind]
		+', name: '+this.name
		+', stringName: '+this.stringName
		+']';	
	}
}

internal final class ASNsSet {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var id:uint;
	public var namespaces:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASNsSet(id:uint = 0) {
		this.id = id;
		this.namespaces = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASNsSet id: '+this.id
		+', namespace ids: '+this.namespaces.join(', ')
		+']';
	}
}

internal final class ASMultiname {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const QNAME:uint = 0x07;
	public static const QNAME_A:uint = 0x0D;
	public static const RTQNAME:uint = 0x0F;
	public static const RTQNAME_A:uint = 0x10;
	public static const RTQNAME_L:uint = 0x11;
	public static const RTQNAME_LA:uint = 0x12;
	public static const MULTINAME:uint = 0x09;
	public static const MULTINAME_A:uint = 0x0E;
	public static const MULTINAME_L:uint = 0x1B;
	public static const MULTINAME_LA:uint = 0x1C;
	public static const KIND_TABLE:Array = [];
	
	initClassKinds(ASMultiname);

	public var id:uint;
	public var kind:uint;
	public var ns:uint;
	public var nsSet:uint;
	public var name:uint;
	public var stringName:String = '*';	
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASMultiname(id:uint = 0, kind:uint = 0) {
		this.id = id;
		this.kind = kind;
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASMultiname id: '+this.id
		+', kind: '+this.kind.toString(16)+':'+ASMultiname.KIND_TABLE[this.kind]
		+', ns: '+this.ns
		+', nsSet: '+this.nsSet
		+', name: '+this.name
		+', stringName: '+this.stringName
		+']';
	}
}

internal final class ASMethod {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const NEED_ARGUMENTS:uint = 0x01;
	public static const NEED_ACTIVATION:uint = 0x02;
	public static const NEED_REST:uint = 0x04;
	public static const HAS_OPTIONAL:uint = 0x08;
	public static const SET_DXNS:uint = 0x40;
	public static const HAS_PARAM_NAMES:uint = 0x80;					

	public var id:uint;
	public var paramCount:uint;
	public var returnType:uint;
	public var returnTypeMultiName:ASMultiname;
	public var params:Array;
	public var name:uint;
	public var stringName:String;
	public var flags:uint;
	public var optionalParams:Array;
	public var paramNames:Array;

	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASMethod(id:uint = 0) {
		this.id = id;
		this.params = [];
		this.optionalParams = [];
		this.paramNames = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASMethod id: '+this.id
		+', paramCount: '+this.paramCount
		+', returnType: '+this.returnType
		+', returnType string name: '+(this.returnTypeMultiName ? this.returnTypeMultiName.stringName : '*')
		+', params: '+(this.params.length ? '\n\t' + this.params.join('\n\t')+ '\n': ', ')
		+', name: '+this.name
		+', stringName: '+this.stringName
		+', flags:'
		
		+(this.flags & ASMethod.NEED_ARGUMENTS ? ' NEED_ARGUMENTS' : '')
		+(this.flags & ASMethod.NEED_ACTIVATION ? ' NEED_ACTIVATION' : '')
		+(this.flags & ASMethod.NEED_REST ? ' NEED_REST' : '')
		+(this.flags & ASMethod.HAS_OPTIONAL ? ' HAS_OPTIONAL' : '')
		+(this.flags & ASMethod.SET_DXNS ? ' SET_DXNS' : '')
		+(this.flags & ASMethod.HAS_PARAM_NAMES ? ' HAS_PARAM_NAMES' : '')
		
		+', optionalParams: '+(this.optionalParams.length ? '\n\t' + this.optionalParams.join('\n\t')+ '\n': ', ')
		+', paramNames: '+this.paramNames+']';	
	}
}

internal final class ASOptionalParam {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const INT:uint = 0x03;
	public static const UINT:uint = 0x04;
	public static const DOUBLE:uint = 0x06;
	public static const UTF8:uint = 0x01;
	public static const TRUE:uint = 0x0B;
	public static const FALSE:uint = 0x0A;
	public static const NULL:uint = 0x0C;
	public static const UNDEFINED:uint = 0x00;
	public static const NAMESPACE:uint = 0x08;
	public static const PACKAGE_NAMESPACE:uint = 0x16;
	public static const PACKAGE_INTERNAL_NS:uint = 0x17;
	public static const PROTECTED_NAMESPACE:uint = 0x18;
	public static const EXPLICIT_NAMESPACE:uint = 0x19;
	public static const STATIC_PROTECTED_NS:uint = 0x1A;
	public static const PRIVATE_NAMESPACE:uint = 0x05;				
	public static const KIND_TABLE:Array = [];										

	initClassKinds(ASOptionalParam);

	public var val:uint;
	public var kind:uint;
	public var value:*;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASOptionalParam(val:uint = 0, kind:uint = 0) {
		this.val = val;
		this.kind = kind;	
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASOptionalParam kind: '+this.kind.toString(16)+':'+ASOptionalParam.KIND_TABLE[this.kind]
		+', val: '+this.val
		+', value: '+this.value
		+']';	
	}
}

internal final class ASMetadata {

	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var id:uint;
	public var name:uint;
	public var stringName:String;
	public var items:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASMetadata(id:uint = 0, name:uint = 0) {
		this.id = id;
		this.name = name;
		this.items = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASMetadata id: '+this.id
		+', name: '+this.name
		+', stringName: '+this.stringName
		+', items: '+(this.items.length ? '\n\t'+this.items.join('\n\t')+'\n' : '')
		+']';
	}
}

internal final class ASMetadataItem {

	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var key:uint;
	public var value:uint;
	public var keyString:String = '';
	public var valueString:String = '';
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASMetadataItem(key:uint = 0, value:uint = 0) {
		this.key = key;
		this.value = value;
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASMetadataItem key: '+this.key
		+', keyString: '+this.keyString
		+', value: '+this.value
		+', valueString: '+this.valueString
		+']';
	}
}

internal final class ASTrait {

	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const SLOT:uint = 0x0;
	public static const METHOD:uint = 0x1;
	public static const GETTER:uint = 0x2;
	public static const SETTER:uint = 0x3;
	public static const CLASS:uint = 0x4;
	public static const FUNCTION:uint = 0x5;
	public static const CONST:uint = 0x6;
	public static const KIND_TABLE:Array = ['SLOT','METHOD','GETTER','SETTER','CLASS','FUNCTION','CONST'];
	public static const FINAL:uint = 0x1;
	public static const OVERRIDE:uint = 0x2;
	public static const METADATA:uint = 0x4;
	
	public var name:uint;
	public var multiName:ASMultiname;
	public var kind:uint;
	public var flags:uint;
	public var metadata:Array;
	public var slotID:uint;
	public var typeName:uint;
	public var typeMultiname:ASMultiname;
	public var vIndex:uint;
	public var vKind:uint;
	public var vValue:*;
	public var classID:uint;
	public var functionID:uint;
	public var dispID:uint;
	public var methodID:uint;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASTrait() {
		this.metadata = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASTrait name: '+this.name
		+', string name: '+(this.multiName ? this.multiName.stringName : '*')
		+', kind: '+ASTrait.KIND_TABLE[this.kind]
		+', flags: '
		
		+(this.flags & ASTrait.FINAL ? 'FINAL ' : '')
		+(this.flags & ASTrait.OVERRIDE ? 'OVERRIDE ' : '')
		+(this.flags & ASTrait.METADATA ? 'METADATA ' : '')
		
		+', metadata: '+this.metadata
		+', slotID: '+this.slotID
		+', typeName: '+this.typeName
		+', string typeName: '+(this.typeMultiname ? this.typeMultiname.stringName : '*')
		+', vIndex: '+this.vIndex
		+', vKind: '+this.vKind
		+', vValue: '+this.vValue
		+', classID: '+this.classID
		+', functionID: '+this.functionID
		+', dispID: '+this.dispID
		+', methodID: '+this.methodID
		+' ]';		
	}
}

internal final class ASInstance {

	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public static const SEALED:uint = 0x01;
	public static const FINAL:uint = 0x02;
	public static const INTERFACE:uint = 0x04;
	public static const PROTECTED:uint = 0x08;
	
	public var id:uint;
	public var name:uint;
	public var multiName:ASMultiname = null;
	public var superName:uint;
	public var multiSuperName:ASMultiname = null;
	public var flags:uint;
	public var ns:uint;
	public var nameSpace:ASNamespace = null;
	public var interfaces:Array;
	public var iinit:uint;
	public var traits:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASInstance(id:uint = 0, name:uint = 0, superName:uint = 0, flags:uint = 0) {
		this.id = id;
		this.name = name;
		this.superName = superName;
		this.flags = flags;
		this.interfaces = [];
		this.traits = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASInstance id: '+this.id
		+', name: '+this.name
		+', string name: '+(this.multiName ? this.multiName.stringName:'null')
		+', superName: '+this.superName
		+', string superName: '+(this.multiSuperName ? this.multiSuperName.stringName:'null')
		+', flags: '
		
		+(this.flags & ASInstance.SEALED ? 'SEALED ':'')
		+(this.flags & ASInstance.FINAL ? 'FINAL ':'')
		+(this.flags & ASInstance.INTERFACE ? 'INTERFACE ':'')
		+(this.flags & ASInstance.PROTECTED ? 'PROTECTED ':'')
		
		+', ns: '+this.ns
		+', string namespace name: '+(this.nameSpace ? this.nameSpace.stringName : 'null')
		+', interfaces: '+this.interfaces
		+', iinit: '+this.iinit
		+', traits: '+(this.traits.length ? '\n\t' + this.traits.join('\n\t')+ '\n': '')
		+']';
	}
}

internal final class ASClass {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var id:uint;
	public var cinit:uint;
	public var traits:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASClass(id:uint = 0, cinit:uint = 0) {
		this.id = id;
		this.cinit = cinit;
		this.traits = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASClass id: '+this.id
		+', cinit: '+this.cinit
		+', traits: '+(this.traits.length ? '\n\t' + this.traits.join('\n\t')+ '\n': '')
		+']';
	}
}

internal final class ASScript {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var id:uint;
	public var init:uint;
	public var traits:Array;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASScript(id:uint = 0, init:uint = 0) {
		this.id = id;
		this.init = init;
		this.traits = [];
	}

	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASScript id: '+this.id
		+', init: '+this.init
		+', traits: '+(this.traits.length ? '\n\t' + this.traits.join('\n\t')+ '\n': '')
		+']';
	}
}

internal final class ASMethodBody {
	
	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var id:uint;
	public var method:uint;
	public var methodName:String;
	public var maxStack:uint;
	public var localCount:uint;
	public var initScopeDepth:uint;
	public var maxScopeDepth:uint;
	public var code:SWFByteArray;
	public var exceptions:Array;
	public var traits:Array;
	public var traitsSlotMap:Array
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASMethodBody(id:uint = 0, method:uint = 0) {
		this.id = id;
		this.method = method;
		this.code = new SWFByteArray();
		this.exceptions = [];
		this.traits = [];
		this.traitsSlotMap = [];
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASMethodBody id: '+this.id
		+', method: '+this.method
		+', methodName: '+this.methodName
		+', maxStack: '+this.maxStack
		+', localCount: '+this.localCount
		+', initScopeDepth: '+this.initScopeDepth
		+', maxScopeDepth: '+this.maxScopeDepth
		+' code length: '+this.code.length
		+', exceptions: '+(this.exceptions.length ? '\n\t'+this.exceptions.join('\n\t')+'\n':', ')
		+'traits: '+(this.traits.length ? '\n\t' + this.traits.join('\n\t')+ '\n': '')
		+']';
	}
}

internal final class ASException {

	/*
	* *********************************************************
	* CLASS PROPERTIES
	* *********************************************************
	*
	*/
	public var from:uint;
	public var to:uint;
	public var target:uint;
	public var excType:uint;
	public var excTypeName:String = '*';
	public var name:uint;
	public var stringName:String;
	
	/*
	* *********************************************************
	* CONSTRUCTOR
	* *********************************************************
	*
	*/
	public function ASException() {
		// empty constructor
	}
	
	/*
	* *********************************************************
	* PUBLIC METHODS
	* *********************************************************
	*
	*/
	public function toString():String {
		return '[ASException from: '+this.from
		+', to: '+this.to
		+', target: '+this.target
		+', excType: '+this.excType
		+', excTypeName: '+this.excTypeName
		+', name: '+this.name
		+', stringName: '+this.stringName
		+']';
	}
}